using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace WindowsGame
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        int windowWidth;
        int windowHeight;

        //Load different used components
        MapComponent map;

        Camera2DComponent camera;
        CameraMovementComponent cameraMovement;

        CollectibleComponents.CollectibleSpawnerComponent collectibleSpawner;

        PlayerComponent player;

        ScoreComponent score;
        ScoreComponents.TimerComponent timer;

        GameStateComponent gameState;

        Menus.StartMenuComponent startMenu;

        Random rnd = new Random();
        bool dubstepMode = false;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            //windowWidth = 900;
            //windowHeight = 600;

            windowWidth = 990;
            windowHeight = 660;

            graphics.PreferredBackBufferWidth = windowWidth;
            graphics.PreferredBackBufferHeight = windowHeight;

            graphics.ApplyChanges();

            //Initialize camera component
            camera = new Camera2DComponent(this);
            camera.Initialize();
            camera.setPosition(new Vector2(0, 555));
            //Camera movement controller
            cameraMovement = new CameraMovementComponent(this);
            cameraMovement.Initialize();

            //Create new player and initialize
            player = new PlayerComponent(this);
            player.Initialize();

            //Initialize map component
            map = new MapComponent(this, camera, player);
            map.Initialize();

            //Create new collectible spawner
            collectibleSpawner = new CollectibleComponents.CollectibleSpawnerComponent(this, map);

            //Create new score component and initialize
            score = new ScoreComponent(this, windowWidth, windowHeight, player, camera);
            score.Initialize();

            //Create new timer component and initialize
            timer = new ScoreComponents.TimerComponent(this, windowWidth, windowHeight);
            timer.Initialize();

            //Create game state and initialize
            gameState = new GameStateComponent(this);
            gameState.Initialize();

            //Create start menu and initialize
            startMenu = new Menus.StartMenuComponent(this, gameState, windowHeight, windowWidth);
            startMenu.Initialize();

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            //Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //Load map component assets
            map.LoadContent(Content, collectibleSpawner);

            //Initialize after loading map, to get bounds
            collectibleSpawner.LoadContent(Content);
            collectibleSpawner.Initialize();

            //Load player content
            player.LoadContent(Content);

            //Load score component content
            score.LoadContent(Content);

            //Load timer component content
            timer.LoadContent(Content);

            //Load start menu content
            startMenu.LoadContent(Content);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();


            #region Not to be used
            //Debugging only
            //---------------------------------------------------------------------------------

            /*
            KeyboardState keyState = Keyboard.GetState();
            float cameraSpeed = 1;
            if (keyState.IsKeyDown(Keys.LeftShift) || keyState.IsKeyDown(Keys.RightShift))
                cameraSpeed = 5;
            if (keyState.IsKeyDown(Keys.Right) && camera.getPosition().X + windowWidth < map.getMapDimensions().X)
                camera.setPosition(new Vector2(camera.getPosition().X + cameraSpeed, camera.getPosition().Y));
            if (keyState.IsKeyDown(Keys.Left) && camera.getPosition().X > 0)
                camera.setPosition(new Vector2(camera.getPosition().X - cameraSpeed, camera.getPosition().Y));
            else if (camera.getPosition().X < 0)
                camera.setPosition(new Vector2(0, camera.getPosition().Y));

            if (keyState.IsKeyDown(Keys.Up) && camera.getPosition().Y > 0)
                camera.setPosition(new Vector2(camera.getPosition().X, camera.getPosition().Y - cameraSpeed));
            if (keyState.IsKeyDown(Keys.Down) && camera.getPosition().Y + windowHeight < map.getMapDimensions().Y)
                camera.setPosition(new Vector2(camera.getPosition().X, camera.getPosition().Y + cameraSpeed));
            else if (camera.getPosition().Y < 0)
                camera.setPosition(new Vector2(camera.getPosition().X, 0));

            if (keyState.IsKeyDown(Keys.Z))
                camera.setZoom(2);
            else if (camera.getZoom() != 1)
                camera.setZoom(1);

            if (keyState.IsKeyDown(Keys.D))
            {
                camera.setPosition(new Vector2(camera.getPosition().X - rnd.Next(-1, 2), camera.getPosition().Y - rnd.Next(-1, 2)));
                map.discoMode(gameTime);
                dubstepMode = true;
            }
            else
                dubstepMode = false;

            if (keyState.IsKeyDown(Keys.W))
                map.drawBounds = true;
            else
                map.drawBounds = false;

             * 
             */
            //----------------------------------------------------------------------------------
            #endregion

            

            //
            //Below this is code that is going to be used
            //

            //Only do these things if game state is playing
            if (gameState.GetCurrentGameState() == GameStateComponent.GameStates.Play)
            {
                //Update camera
                cameraMovement.Update(gameTime, player, map, camera, windowWidth, windowHeight);
                camera.Update(gameTime, windowWidth, windowHeight);

                //Quick way of spawning collectibles outside of camera
                if (rnd.Next(0, 150) == 1)
                    collectibleSpawner.Spawn(CollectibleComponents.CollectibleComponent.CollectibleType.PaperBag,
                        new Vector2(camera.getPosition().X + windowWidth + 128, camera.getPosition().Y + rnd.Next(windowHeight / 2, windowHeight)),
                        rnd.Next(1, 3));

                //Update player component
                player.Update(gameTime, map, collectibleSpawner, score, camera);

                //Update score component
                score.Update(gameTime);

                //Update timer component
                timer.Update(gameTime);

                //Update collectibles
                for (int i = 0; i < collectibleSpawner.GetCollectibleList().Count; i++)
                {
                    collectibleSpawner.GetCollectibleList()[i].Update(gameTime);

                    //Despawn if outside camera
                    if (collectibleSpawner.GetCollectibleList()[i].GetPosition().X < camera.getPosition().X - 128)
                        collectibleSpawner.Despawn(i);
                }

                //Update map component
                map.Update(gameTime);
            }
            else if (gameState.GetCurrentGameState() == GameStateComponent.GameStates.Menu)
            {
                if (gameState.GetOldGameState() != gameState.GetCurrentGameState())
                {
                    //If this is true that means that a game state has been changed recently. Change map to map to show on menu
                    if (rnd.Next(0, 2) == 1)
                        map.changeMap("map1_1");
                    else
                        map.changeMap("map1_2");
                }


                //Update camera
                camera.Update(gameTime, windowWidth, windowHeight);

                //Update start menu
                startMenu.Update(gameTime, camera, map, collectibleSpawner);

                //Update map component
                map.Update(gameTime);
            }

            //Update gamestate component to store old game state
            gameState.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.SlateGray);

            #region Not to be used
            if (dubstepMode)
                GraphicsDevice.Clear(new Color(rnd.Next(0,255), rnd.Next(0,255), rnd.Next(0,255)));
            #endregion

            //Do not draw anything if map is loading. This will result in a blank screen i loading
            if (!map.isLoading())
            {
                //Only do following if game state is play
                if (gameState.GetCurrentGameState() == GameStateComponent.GameStates.Play)
                {
                    //Draw map. Pass gametime, spritebatch and camera
                    map.Draw(gameTime, spriteBatch, camera);

                    //Draw other graphics here
                    collectibleSpawner.Draw(gameTime, spriteBatch, camera);

                    //Draw player
                    player.Draw(spriteBatch, camera);

                    //Use late draw in map component for other layers and such
                    map.LateDraw(gameTime, spriteBatch, camera);

                    //Draw score HUD
                    score.Draw(spriteBatch);

                    //Draw timer HUD
                    timer.Draw(spriteBatch);
                }
                else if (gameState.GetCurrentGameState() == GameStateComponent.GameStates.Menu)
                {
                    //Draw map. Pass gametime, spritebatch and camera
                    map.Draw(gameTime, spriteBatch, camera);
                    map.LateDraw(gameTime, spriteBatch, camera);

                    //Draw start menu
                    startMenu.Draw(spriteBatch);
                }
            }

            base.Draw(gameTime);
        }
    }
}
